package org.jvm.nativemethod.methods.sun.misc;

import java.util.*;

/**
 * @author 海燕
 * @date 2023/3/2
 */
public class Malloc {

    private static Map<Long, byte[]> allocated = new HashMap<>();

    private static long nextAddress = 64;

    /**
     * 申请一个内存块，大小size个byte
     *
     * @param size
     * @return 内存块起始位置
     */
    public static long allocate(long size) {
        byte[] mem = new byte[(int) size];
        long address = nextAddress;
        allocated.put(address, mem);
        nextAddress += size;
        return address;
    }

    public static long reallocate(long address, long size) {
        if (size == 0) {
            return 0;
        }
        if (address == 0) {
            return allocate(size);
        }
        MemIndex memIndex = memoryAt(address);
        long start = memIndex.getStart();
        byte[] mem = memIndex.getMem();
        if (mem.length - start >= size) {
            return address;
        } else {
            allocated.remove(address);
            long newAddress = allocate(size);
            //TODO 我理解这里不用copy？
            MemIndex newMem = memoryAt(newAddress);
            for (int i = 0; i < mem.length; i++) {
                newMem.getMem()[i] = mem[(int) start];
                start++;
                if (start == mem.length) {
                    break;
                }
            }
            return newAddress;
        }
    }

    /**
     * 地址必须是内存块开头
     *
     * @param address
     */
    public static void free(long address) {
        allocated.remove(address);
    }

    public static MemIndex memoryAt(long address) {
        for (Map.Entry<Long, byte[]> entry : allocated.entrySet()) {
            Long startAddress = entry.getKey();
            byte[] mem = entry.getValue();
            if (address >= startAddress && address < startAddress + mem.length) {
                long offset = address - startAddress;
                return new MemIndex(offset, mem);
            }
        }
        return null;
    }

    public static void putLong(long address, long value) {
        MemIndex memIndex = memoryAt(address);
        int start = (int) memIndex.getStart();
        byte[] mem = memIndex.getMem();
        for (int i = 0; i < 8; i++) {
            mem[start + i] = (byte) (value >>> (56 - i * 8));
        }
    }


    static class MemIndex {

        private long start;

        private byte[] mem;

        public MemIndex(long start, byte[] mem) {
            this.start = start;
            this.mem = mem;
        }

        public long getStart() {
            return start;
        }

        public void setStart(long start) {
            this.start = start;
        }

        public byte[] getMem() {
            return mem;
        }

        public void setMem(byte[] mem) {
            this.mem = mem;
        }
    }
}
